﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace Flagwind.Data
{
	/// <summary>
	/// 数据映射类。
	/// </summary>
	public class DataMapping
	{
		#region 常量定义

		private const string NODE_GROUP_PATH = "/mapping/commands[@name='@@GroupName']/*[@name='@@CommandName']";
		private const string NODE_ITEM_PATH = "/mapping/commands/*[@name='@@CommandName']";

		#endregion

		#region 私有字段

		private XmlDocument _document;

		#endregion

		#region 保护属性

		/// <summary>
		/// 获取数据映射文档。
		/// </summary>
		protected XmlDocument Document
		{
			get
			{
				if(_document == null)
				{
					_document = this.loadDocument();
				}
				return _document;
			}
		}

		#endregion

		#region 公共属性

		/// <summary>
		/// 获取或设置映射名称。
		/// </summary>
		public string Name
		{
			get;
			protected set;
		}

		/// <summary>
		/// 获取或设置映射路径。
		/// </summary>
		public string FilePath
		{
			get;
			protected set;
		}

		#endregion

		#region 构造方法

		public DataMapping(string name, string filePath)
		{
			if(string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentNullException("name");
			}
			if(string.IsNullOrWhiteSpace(filePath))
			{
				throw new ArgumentNullException("filePath");
			}
			this.Name = name;
			this.FilePath = filePath;
		}

		#endregion

		#region 公共方法

		public DataCommand GetCommand(string commandName)
		{
			return this.GetCommand(null, commandName, DataProviderFactory.GetProvider());
		}

		public DataCommand GetCommand(string commandName, IDataProvider dataProvider)
		{
			return this.GetCommand(null, commandName, dataProvider);
		}

		public DataCommand GetCommand(string groupName, string commandName)
		{
			return this.GetCommand(groupName, commandName, DataProviderFactory.GetProvider());
		}

		public DataCommand GetCommand(string groupName, string commandName, IDataProvider dataProvider)
		{
			if(string.IsNullOrWhiteSpace(commandName))
			{
				throw new ArgumentNullException("commandName");
			}
			var root = this.Document.DocumentElement;
			var path = string.Empty;
			if(string.IsNullOrEmpty(groupName))
			{
				path = NODE_ITEM_PATH.Replace("@@CommandName", commandName);
			}
			else
			{
				path = NODE_GROUP_PATH.Replace("@@GroupName", groupName).Replace("@@CommandName", commandName);
			}
			var node = root.SelectSingleNode(path);
			if(node == null || !node.HasChildNodes)
			{
				throw new ArgumentException(string.Format("无法获取指定命令组 '{0}' 中的 '{1}' 命令。", groupName, commandName));
			}
			var commandType = (CommandType)Enum.Parse(typeof(CommandType), node.Attributes["type"].Value);
			var command = new DataCommand(node.InnerText, commandType, dataProvider);
			command.CommandName = commandName;
			return command;
		}

		#endregion

		#region 私有方法

		private XmlDocument loadDocument()
		{
			string content = null;
			XmlDocument document = null;
			// 以资源文件方式获取。
			if(this.FilePath.StartsWith("@R://"))
			{
				string[] source = this.FilePath.Substring(5).Split(',');
				if(source.Length != 2)
				{
					throw new Exception("嵌入的 Mapping 文件的申明应该以 \"程序集命名空间.文件名.config, 程序集名称\" 的格式 ");
				}
				string assemblyName = source[1].Trim();
				string resourceName = source[0].Trim();
				content = Utility.GetAssemblyResource(assemblyName, resourceName);
			}
			else
			{
				var filePath = this.GetFilePath(this.FilePath);
				using(var reader = new StreamReader(filePath))
				{
					content = reader.ReadToEnd();
				}
			}
			document = new XmlDocument();
			document.LoadXml(content);
			return document;
		}

		private string GetFilePath(string filePath)
		{
			filePath = filePath.Replace((Path.DirectorySeparatorChar == '/' ? '\\' : '/'), Path.DirectorySeparatorChar).Trim();
			if(filePath[0] == '/' || filePath[0] == '\\')
			{
				return Path.Combine(Path.GetPathRoot(AppDomain.CurrentDomain.BaseDirectory), filePath.Substring(1));
			}
			string directoryPath;
			if(filePath.StartsWith("~/") || filePath.StartsWith("~\\"))
			{
				directoryPath = Path.GetDirectoryName(filePath.Substring(2));
			}
			else
			{
				directoryPath = Path.GetDirectoryName(filePath);
			}
			return Path.Combine(directoryPath, Path.GetFileName(filePath));
		}

		#endregion
	}
}